import * as THREE from 'three'
import { EditorModels } from './EditorModels';
export interface IEditorHistoryStore {
    editorModels: EditorModels
}
interface uuidObj<T> {
    [uuid: string]: T
}
export default class EditorHistoryStore {
    option: IEditorHistoryStore;
    editorModels: EditorModels;
    geometries: uuidObj<THREE.BufferGeometry> = {};
    materials: uuidObj<THREE.Material> = {};
    materialsRefCounter = new Map<THREE.Material, number>();
    constructor(option: IEditorHistoryStore) {
        this.option = option
        this.editorModels = option.editorModels
    }
    addObject(object: THREE.Object3D, parent?: THREE.Object3D, index?: number) {
        object.traverse((child: THREE.Object3D) => {
            if (child instanceof THREE.Mesh) {
                if (child.geometry !== undefined) this.addGeometry(child.geometry);
                if (child.material !== undefined) this.addMaterial(child.material);
            }

        });
        if (parent === undefined) {
            this.editorModels.scene.add(object);
        } else {
            if (!index) return
            parent.children.splice(index, 0, object);
            object.parent = parent;
        }
    }
    removeObject(object: THREE.Object3D) {
        if (object.parent === null) return; // avoid deleting the camera or scene
        object.traverse((child) => {
            if (child instanceof THREE.Mesh) {
                this.removeMaterial(child.material);
            }
        });
        object.parent.remove(object);
    }

    addGeometry(geometry: THREE.BufferGeometry) {
        this.geometries[geometry.uuid] = geometry;
    }

    addMaterial(material: THREE.Material | THREE.Material[]) {
        if (Array.isArray(material)) {
            for (var i = 0, l = material.length; i < l; i++) {
                this.addMaterialToRefCounter(material[i]);
            }
        } else {
            this.addMaterialToRefCounter(material);
        }
    }

    addMaterialToRefCounter(material: THREE.Material) {
        let materialsRefCounter = this.materialsRefCounter;
        let count = materialsRefCounter.get(material);
        if (count === undefined) {
            materialsRefCounter.set(material, 1);
            this.materials[material.uuid] = material;
        } else {
            count++;
            materialsRefCounter.set(material, count);
        }
    }

    removeMaterial(material: THREE.Material | THREE.Material[]) {
        if (Array.isArray(material)) {
            for (var i = 0, l = material.length; i < l; i++) {
                this.removeMaterialFromRefCounter(material[i]);
            }
        } else {
            this.removeMaterialFromRefCounter(material);
        }
    }

    removeMaterialFromRefCounter(material: THREE.Material) {
        let materialsRefCounter = this.materialsRefCounter;
        let count = materialsRefCounter.get(material);
        if (!count) return;
        count--;
        if (count === 0) {
            materialsRefCounter.delete(material);
            delete this.materials[material.uuid];
        } else {
            materialsRefCounter.set(material, count);
        }

    }
}